namespace taurus.client {

    /**
     * 网络协议数据包
     * @author daixiwei	daixiwei15@126.com
     *
     */
    export class Packet {
        /**
         * 协议包ID
         */
        public actionID: number;
        /**
         * 参数数据
         */
        public parameters: ITObject;
    }

    class Callback {
        public readonly thisObject: any;
        public readonly func: Function;

        public constructor(_thisObject: any, _func: Function) {
            this.thisObject = _thisObject;
            this.func = _func;
        }
    }

    /**
     * SocketEngine
     */
    export class SocketEngine implements IScoketLayer {
        _socket: WebSocket;
        private _state: SocketState;
        private _sessionToken: string;
        private _secure: boolean;
        _packetHandler: IOHandler;
        _controller: SystemController;
        _onConnect: Callback;
        _onEvent: Callback;
        _lagMonitor: LagMonitor;
        _client:TaurusClient

        public constructor(client:TaurusClient,secure: boolean) {
            this._state = SocketState.Disconnected;
            this._controller = new SystemController(this);
            this._packetHandler = new IOHandler(this);
            this._lagMonitor = new LagMonitor(this, NetManager.TIMEOUT_TIME, 10);
            this._secure =secure;
            this._client = client;
        }

        public connect(ip: string, game: string): boolean {
            var self = this;
            if (this._state != SocketState.Disconnected) {
                log.error("Calling connect when the socket is not disconnected");
                return false;
            }
            this._state = SocketState.Connecting;
            this._socket = new WebSocket(this._secure?"wss":"ws" + "://" + ip + "/websocket");
            this._socket.binaryType = "arraybuffer";
            this._socket.onopen = function (ev) {
                self._state = SocketState.Connected;
                self.onHandshake();
            };
            this._socket.onmessage = function (ev) {
                if (ev.data instanceof ArrayBuffer) {
                    let bytes = new ByteArray(ev.data);
                    self._packetHandler.onDataRead(bytes);
                }
            };
            this._socket.onclose = function (ev) {
                if(NetManager.debug_print){
                    log.debug("onclose: wasClean ={0}, code={1}, reason={2}", ev.wasClean, ev.code, ev.reason);
                }
                if (self._state != SocketState.Connecting) {
                    if (ev.code == 1000) {
                        self.onStatusChanged(SocketCode.Disconnect);
                    } else if (ev.code == 1001) {
                        self.onStatusChanged(SocketCode.DisconnectByServer);
                    } else if (1006 == ev.code) {
                        self.onStatusChanged(SocketCode.TimeoutDisconnect);
                    } else if (ev.code > 1000) {
                        self.onStatusChanged(SocketCode.ExceptionOnReceive);
                    }
                }
            };
            this._socket.onerror = function (ev) {
                self._onError(ev);
            };
        }

        private _onError(ev: any) {
            log.error("Connection error:", ev);
            if(this._state == SocketState.Connecting){
                this.onStatusChanged(SocketCode.ExceptionOnConnect);
            }else{
                this.onStatusChanged(SocketCode.Exception);
            }
        }

        onStatusChanged(statusCode: SocketCode) {
            if (statusCode != SocketCode.Connect) {
                this.disconnect();
            }
            let cb = this._onConnect;
            cb.func.call(cb.thisObject, statusCode);
        }

        onHandshake() {
            let cb = this._onConnect;
            cb.func.call(cb.thisObject, SocketCode.Connect);
            if (this._lagMonitor != null) {
                this._lagMonitor.start();
            }
        }

        public disconnect(): boolean {
            if (this._state == SocketState.Disconnected) return false;
            if (this._lagMonitor != null)
                this._lagMonitor.destroy();
            this._lagMonitor = null;
            this._state = SocketState.Disconnecting;
            this._socket.close();
        }

        public write(msg: Message) {
            this._controller.sendRequest(msg);
        }

        public getProtocol(): ConnectionProtocol {
            return ConnectionProtocol.WebSocket;
        }

        public setOnConnect(func: (code: SocketCode) => void, thisObject: any) {
            this._onConnect = new Callback(thisObject, func);
        }

        public setOnEvent(func: (msg: Message) => void, thisObject: any) {
            this._onEvent = new Callback(thisObject, func);
        }

        public getAveragePingTime(): number {
            return this._lagMonitor != null ? this._lagMonitor.getAveragePingTime() : 300;
        }

        public getSession(): string {
            return this._sessionToken;
        }

        public setSession(session: string) {
            this._sessionToken = session
        }

        public getProtocolCompression():number{
            return this._client.getProtocolCompression();
        }

    }
}